package edu.ucsd.cs110.mycity.services;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.RosterListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.filter.MessageTypeFilter;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.util.StringUtils;

import edu.ucsd.cs110.mycity.MyCity;
import edu.ucsd.cs110.mycity.MyCityMapActivity;
import edu.ucsd.cs110.mycity.NotificationFactory;
import edu.ucsd.cs110.mycity.SoundFacade;
import edu.ucsd.cs110.mycity.controller.ChatController;
import edu.ucsd.cs110.mycity.controller.CoordinateController;
import edu.ucsd.cs110.mycity.controller.ViewController;
import edu.ucsd.cs110.mycity.controller.XMPPController;
import edu.ucsd.cs110.mycity.model.data.TRKPTCoordinate;

import android.app.Service;
import android.content.Intent;
import android.location.Location;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.IBinder;
import android.util.Log;

public class ChatService extends Service {
	/**
	 * ChatService should implement packetlisteners and anything related to XMPP
	 * chat in the background 
	 */

	private static ChatPacketListener chatPacketListener;
	private static ChatRosterListener chatRosterListener = new ChatRosterListener();
	
	private Map<String,ArrayList<ChatServiceListener>> listeners = new HashMap<String,ArrayList<ChatServiceListener>>();
	
	/*
	 * Pre:  - listener != null
	 *       - ! isListening(listener)
	 * Post: - isListening(listener)
	 */
	public void addChatServiceListener(ChatServiceListener listener) {
		Log.i("ChatService", "Added new ChatServiceListener");
		String u = listener.getUsername();
		ArrayList<ChatServiceListener> l;
		if (!listeners.containsKey(u)) {
			l = new ArrayList<ChatServiceListener>();
			listeners.put(u, l);
		} else {
			l = listeners.get(u);
		}
		l.add(listener);
	}
	
	/*
	 * Pre:  - listener != null
	 *       - isListening(listener)
	 * Post: - ! isListening(listener)
	 */
	public void removeChatServiceListener(ChatServiceListener listener) {
		String u = listener.getUsername();
		if ( listeners.containsKey(u) ) {
			listeners.get(u).remove(listener);
		}
	}
	
	/*
	 * Pre:  - listener != null 
	 * Post: - true iff addChatServiceListener(listener) has been called more times than removeChatServiceListener(listener)   
	 */
	public boolean isListening(ChatServiceListener listener) {
		return (listeners.containsKey(listener.getUsername()));
	}
	
	/*
	 * Pre:  - there exists some ChatServiceListener "listener" such that listener.getUsername.equals(username)
	 *       - username != null
	 *       - isListening(listener)
	 * Post: - for all ChatServiceListeners "listener" such that isListening(listener) == true, listener.fire() is called.     
	 */
	public void fire(String username) {
		if ( listeners.containsKey(username) ) {
			ArrayList<ChatServiceListener> l = listeners.get(username);
			Log.i("ChatService","Firing events on all listeners for '" + username + "' Size: " + l.size());
			Iterator<ChatServiceListener> iter = l.iterator();
			while (iter.hasNext()) {
				ChatServiceListener c = iter.next();
				c.fire();
			}
		}
	}
	
	/*
	 * Pre:  - onCreate() has not yet been called
	 *       - internal variable values unspecified
	 * Post: - internal variables set 
	 */
	@Override
	public void onCreate() {
		this.chatPacketListener = new ChatPacketListener();
		XMPPController.setChatService(this);
		ChatController.refresh();
		super.onCreate();
	}

	/*
	 * Pre:  - none 
	 * Post: - none   
	 */
	@Override
	public IBinder onBind(Intent arg0) {
		return null;
	}
	
	/*
	 * Pre:  - none
	 * Post: - ChatService started   
	 */
	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		XMPPConnection connection = XMPPController.getConnection();
		PacketFilter filter = new MessageTypeFilter(Message.Type.chat);
		connection.addPacketListener(chatPacketListener, filter);
		connection.getRoster().addRosterListener(chatRosterListener);
		Log.i("ChatService", "Started chat service");
		return Service.START_NOT_STICKY;
	}
	
	/*
	 * Pre:  - none 
	 * Post: - important listeners unregistered   
	 */
	@Override
	public void onDestroy() {
		XMPPController.getConnection().removePacketListener(chatPacketListener); 
		XMPPController.getConnection().getRoster().removeRosterListener(chatRosterListener);
	}

	/*
	 * Internal classes do not require design by contract comments,
	 * as they are not intended for use outside the package/class they are defined in.
	 */
	class ChatPacketListener implements PacketListener {

		@Override
		public void processPacket(Packet packet) {
			Message message = (Message) packet;
			if (message.getBody() != null) {
				String fromName = StringUtils.parseBareAddress(message
						.getFrom());
				String chatContent = message.getBody();
				String pattern = "<trkpt lat=\"([-0-9]+).([0-9]+)\" lon=\"([-0-9]+).([0-9]+)\">\\s*(<ele>[0-9]+</ele>)?<time>(.*)</time>\\s*</trkpt>";
				if (chatContent.matches(pattern)) {
					Log.i("ChatService","Received location message");
					Pattern p = Pattern.compile(pattern);
					Matcher regexMatcher = p.matcher(chatContent);
					if (regexMatcher.find()) {
						Double lat = Double.parseDouble(regexMatcher.group(1)
								+ "." + regexMatcher.group(2));
						Double lon = Double.parseDouble(regexMatcher.group(3)
								+ "." + regexMatcher.group(4));
						String time = regexMatcher.group(6);
						TRKPTCoordinate t = new TRKPTCoordinate(lat, lon, time);
						CoordinateController
								.updateCoordinates(StringUtils
										.parseBareAddress(message.getFrom()), t);
					}
				} else {
					ChatController.addToLog(fromName,
							StringUtils.parseBareAddress(message.getFrom())
									+ ": " + chatContent);
					Log.i("ChatService", "Received message from " + fromName);
					if ( XMPPController.getChatService() != null ) {
						XMPPController.getChatService().fire(fromName);
					}
					if ( MyCity.isPaused() ) {
						NotificationFactory.createNewMessageNotification(ChatService.this, StringUtils.parseBareAddress(message.getFrom()), chatContent);
						SoundFacade.playNotificationSound(getApplicationContext());
					}
				}
			}
		}
	}
	
	public static class SendLocationInfoTask extends AsyncTask<Void,Void,Void> {

		@Override
		protected Void doInBackground(Void... params) {
			XMPPController.refreshBuddies();
			ArrayList<HashMap<String,String>> m = XMPPController.getRosterMap();
			Iterator<HashMap<String,String>> i = m.iterator();
			while ( i.hasNext() ) {
				HashMap<String,String> h = i.next();
				String username = h.get("username");
				String presence = h.get("presence");
				if (presence.substring(0, 9).equals("available")) {
					Log.i("ChatService","Sending location message to " + username);
					MyCityMapActivity mc = (MyCityMapActivity) ViewController.getMapScreen();
					Location c = mc.currentLocation;
					Double latitude = c.getLatitude();
					Double longitude = c.getLongitude();
					String message = "<trkpt lat=\"" + latitude + "\" lon=\"" + longitude + "\"><time>123213</time></trkpt>";
					Message msg = new Message(username, Message.Type.chat);  
		            msg.setBody(message);
		            XMPPController.getConnection().sendPacket(msg);
				} else {
					Log.i("ChatService",username + " was unavailable");
				}
			}
			return null;
		}
		
	}
	
	private static class ChatRosterListener implements RosterListener {

		public void entriesAdded(Collection<String> addresses) {
			XMPPController.refreshBuddies();
			ChatController.refresh();
		}
		
	    public void entriesDeleted(Collection<String> addresses) {
	    	XMPPController.refreshBuddies();
	    	ChatController.refresh();
	    }
	    
	    public void entriesUpdated(Collection<String> addresses) {
	    	XMPPController.refreshBuddies();
	    	ChatController.refresh();
	    }
	    
	    public void presenceChanged(Presence presence) {
	    	XMPPController.refreshBuddies();
	    	ChatController.refresh();
	    	MyCityMapActivity m = (MyCityMapActivity) ViewController.getMapScreen();
	    	m.drawBuddies();
	    }
		
	}
	
}
